﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
using System.Web;
using API.Helpers;
using Microsoft.IdentityModel.Claims;

namespace POS.API.Helpers
{
    public class ClaimsIdentityManager : IClaimsManager
    {
        private Microsoft.IdentityModel.Claims.IClaimsPrincipal CreateClaimsIdentity(string username)
        {
            var identity = new GenericPrincipal(new GenericIdentity(username), null);

            var claimsPrincipal = new Microsoft.IdentityModel.Claims.ClaimsPrincipal(identity);

            Thread.CurrentPrincipal = claimsPrincipal;
            HttpContext.Current.User = claimsPrincipal; //bug in asp.net http://aspnetwebstack.codeplex.com/workitem/264
            return claimsPrincipal;
        }

        private Microsoft.IdentityModel.Claims.IClaimsIdentity GetClaimsIdentity()
        {
            var claimsPrincipal = Thread.CurrentPrincipal as Microsoft.IdentityModel.Claims.ClaimsPrincipal;
            if (claimsPrincipal != null)
            {
                var claimsIdentity = ((Microsoft.IdentityModel.Claims.IClaimsIdentity)claimsPrincipal.Identity);
                return claimsIdentity;
            }
            return null;
        }
        public bool Exists(string key)
        {
            var claimsIdentity = GetClaimsIdentity();
            if (claimsIdentity.Claims.Exists(c => c.ClaimType == key))
                return true;
            else
                return false;
        }

        public void AddClaim(string key, object value)
        {
            AddClaim(key, value.ToString());
        }

        public void AddClaim(string key, string value)
        {
            var claimsIdentity = GetClaimsIdentity();
            if (claimsIdentity.IsAuthenticated && !claimsIdentity.Claims.Exists(c => c.ClaimType == key))
                claimsIdentity.Claims.Add(new Microsoft.IdentityModel.Claims.Claim(key, value));
            else if (claimsIdentity.IsAuthenticated)//update
            {
                //Remove then add -- Might need to change if multiple claim keys are allowed
                claimsIdentity.Claims.Remove(claimsIdentity.Claims.First(c => c.ClaimType == key));
                claimsIdentity.Claims.Add(new Microsoft.IdentityModel.Claims.Claim(key, value));
            }
            else
            {
                throw new ExternalUnauthorizedAccessException("Claims can only be added to a user identity once the user has authenticated.");
            }

            Thread.CurrentPrincipal = Microsoft.IdentityModel.Claims.ClaimsPrincipal.CreateFromIdentity(claimsIdentity);
            HttpContext.Current.User = Thread.CurrentPrincipal;
        }

        public void RemoveClaim(string key)
        {
            var claimsIdentity = GetClaimsIdentity();
            if (claimsIdentity.IsAuthenticated && !claimsIdentity.Claims.Exists(c => c.ClaimType == key))
                return;
            else if (claimsIdentity.IsAuthenticated)
            {
                claimsIdentity.Claims.Remove(claimsIdentity.Claims.First(c => c.ClaimType == key));
            }
            else
            {
                throw new ExternalUnauthorizedAccessException("Claims can only be added to a user identity once the user has authenticated.");
            }

            Thread.CurrentPrincipal = Microsoft.IdentityModel.Claims.ClaimsPrincipal.CreateFromIdentity(claimsIdentity);

        }

        private string RetrieveClaimAsString(string key)
        {
            var claimsIdentity = GetClaimsIdentity();
            if (claimsIdentity.IsAuthenticated)
            {
                if (claimsIdentity.Claims.Exists(c => c.ClaimType == key))
                {
                    var firstOrDefault = claimsIdentity.Claims.FirstOrDefault(c => c.ClaimType == key);
                    if (firstOrDefault != null)
                        return firstOrDefault.Value;
                }
                else
                {
                    throw new ExternalKeyNotFoundException(string.Format("Claim '{0}' cannot be found for authenticated user.", key));
                }
            }
            else
                throw new ExternalUnauthorizedAccessException("Claims can only be retrieved from a user identity once the user has authenticated.");
            return string.Empty;
        }

        public T RetrieveClaim<T>(string key)
        {
            return (T)Convert.ChangeType(RetrieveClaimAsString(key), typeof(T));
        }
    }
}